import formatMessage from "../format-message"
import { changeTag } from "../utils/dom"

/* Headings Sequence rule
 * this rule is ensuring that heading tags (H1-H6) are layed out in sequential
 * order for organizing your site.
 *
 * this rule only looks at H2-H6 headings. all other tags pass.
 * this rule will walk 'up-down' the dom to find the heading tag that is
 * laid out previous to the heading tag being checked.
 * this rule will see if the heading tag number of the previous heading is
 * one more than one less than it's own heading tag and will fail if so
 * this rule will check to see if there is no previous heading tag and will
 * fail the test if so
 */

const isHtag = elem => {
  const allHTags = {
    H1: true,
    H2: true,
    H3: true,
    H4: true,
    H5: true,
    H6: true
  }
  return elem && allHTags[elem.tagName] === true
}

// gets the H tag that is furthest down in the tree from elem(inclusive)
const getHighestOrderHForElem = elem => {
  const allHForElem = Array.prototype.slice.call(
    elem.querySelectorAll("H1,H2,H3,H4,H5,H6")
  )
  if (allHForElem.length > 0) {
    return allHForElem.reverse()[0]
  }
  if (isHtag(elem)) {
    return elem
  }
  return undefined
}

// gets all siblings of elem that come before the elem ordered by nearest to
// elem
const getPrevSiblings = elem => {
  let ret = []
  if (!elem || !elem.parentElement || !elem.parentElement.children) {
    return ret
  }
  const sibs = elem.parentElement.children
  for (var i = 0; i < sibs.length; i++) {
    if (sibs[i] === elem) {
      break
    }
    ret.unshift(sibs[i])
  }
  return ret
}

const searchPrevSiblings = elem => {
  const sibs = getPrevSiblings(elem)
  let ret
  for (let i = 0; i < sibs.length; i++) {
    ret = getHighestOrderHForElem(sibs[i])
    if (ret) {
      break
    }
  }
  return ret
}

const _walkUpTree = elem => {
  let ret
  if (!elem || elem.tagName === "BODY") {
    return undefined
  }
  if (isHtag(elem)) {
    return elem
  }
  ret = searchPrevSiblings(elem)
  if (!ret) {
    ret = _walkUpTree(elem.parentElement)
  }
  return ret
}

const walkUpTree = elem => {
  let ret = searchPrevSiblings(elem)
  if (!ret) {
    ret = _walkUpTree(elem.parentElement)
  }
  return ret
}

const getPriorHeading = elem => {
  return walkUpTree(elem)
}

// a valid prior H tag is greater or equal to one less than current
const getValidHeadings = elem => {
  const hNum = +elem.tagName.substring(1)
  const ret = {}
  for (var i = hNum - 1; i <= 6; i++) {
    ret[`H${i}`] = true
  }
  return ret
}

export default {
  id: "headings-sequence",
  test: elem => {
    const testTags = {
      H2: true,
      H3: true,
      H4: true,
      H5: true,
      H6: true
    }
    if (testTags[elem.tagName] !== true) {
      return true
    }
    const validHeadings = getValidHeadings(elem)
    const priorHeading = getPriorHeading(elem)
    if (priorHeading) {
      return validHeadings[priorHeading.tagName]
    }
    return true
  },

  data: elem => {
    return {
      action: "nothing"
    }
  },

  form: () => [
    {
      label: formatMessage("Action to take:"),
      dataKey: "action",
      options: [
        ["nothing", formatMessage("Leave as is")],
        ["elem", formatMessage("Fix heading hierarchy")],
        ["modify", formatMessage("Remove heading style")]
      ]
    }
  ],

  update: (elem, data) => {
    if (!data || !data.action || data.action === "nothing") {
      return elem
    }
    switch (data.action) {
      case "elem": {
        const priorH = getPriorHeading(elem)
        const hIdx = priorH ? +priorH.tagName.substring(1) : 0
        return changeTag(elem, `H${hIdx + 1}`)
      }
      case "modify": {
        return changeTag(elem, "p")
      }
    }
  },

  message: () => formatMessage("Heading levels should not be skipped."),

  why: () =>
    formatMessage(
      "Sighted users browse web pages quickly, looking for large or bolded headings. Screen reader users rely on headers for contextual understanding. Headers should use the proper structure."
    ),

  link: "https://www.w3.org/TR/WCAG20-TECHS/G141.html",
  linkText: () => formatMessage("Learn more about organizing page headings")
}
